/**
 * MCU 系统时钟适配 API
 * 张勇 41204@qq.com
 */

#include <sys/time.h>
#include "sleep.h"
#include "xtime_l.h"
#include "xparameters.h"
#include "xil_types.h"
#include "sys_clock.h"
#include "xrtcpsu.h"		/* RTCPSU device driver */
#include "xscugic.h"		/* Interrupt controller device driver */

#include "share/log.h"
#define LOGLEVEL LOG_DEBUG
#define LOGTAG   "SYSCLK"
#include "share/log4app.h"

#define RTC_DEVICE_ID			XPAR_XRTCPSU_0_DEVICE_ID
#define INTC_DEVICE_ID			XPAR_SCUGIC_SINGLE_DEVICE_ID
#define RTC_ALARM_INT_IRQ_ID	XPAR_XRTCPSU_ALARM_INTR

static XRtcPsu RtcPsu;		/* Instance of the RTC Device */

// 实现 ANSI C 函数 _gettimeofday()
int _gettimeofday(struct timeval *tv, void *tzvp) {
    tv->tv_sec = osGetS();
    tv->tv_usec = osGetUs() % 1000000;  // get remaining microseconds
    return 0;  // return non-zero for error
}

timeu_t osGetUs() {
	XTime tCur;
	XTime_GetTime(&tCur);
	return tCur * 1000000 / COUNTS_PER_SECOND;
}

timeu_t osGetMs() {
	return osGetUs() / 1000;
}

void osDelayUs(timeu_t x) {
	usleep(x);
}

void osDelayMs(timeu_t x) {
#if USE_FREERTOS
	return vTaskDelay((x) / portTICK_PERIOD_MS);
#else
	usleep(x * 1000);
#endif
}

std_err_t osSetTime(long time) {
	// 因为 XRtcPsu_SetTime() 设置的是从 2000-01-01 00:00:00 开始计算的秒数，
	// 所以如果要转换成从 1970-01-01 00:00:00 开始计算的秒数，需要减去中间的差值
	if(time < 946696320UL) return STD_ERR_INVALID_ARG;
	XRtcPsu_SetTime(&RtcPsu, time - 946696320UL);
	return STD_ERR_OK;
}

long osGetTime() {
	// 因为 XRtcPsu_GetCurrentTime() 获取的是从 2000-01-01 00:00:00 开始计算的秒数，
	// 所以如果要转换成从 1970-01-01 00:00:00 开始计算的秒数，需要加上中间的差值
	return XRtcPsu_GetCurrentTime(&RtcPsu) + 946696320UL;
}

struct tm osGetLocalDateTime() {
	struct tm timeinfo;
	time_t now = time(NULL);
	localtime_r(&now, &timeinfo);
	return timeinfo;
}

char* osGetLocalDateTimeString() {
	static char str[64];
	struct tm timeinfo = osGetLocalDateTime();
	strftime(str, sizeof(str), "%c", &timeinfo);
	return str;
}

std_err_t osSetDateTime(int year, int month, int day, int hour, int minute, int second) {
	XRtcPsu_DT dt = {year, month, day, hour, minute, second};
	XRtcPsu_SetTime(&RtcPsu, XRtcPsu_DateTimeToSec(&dt));
	return STD_ERR_OK;
}

/// @breif 获取时间
std_err_t osGetDateTime(int *year, int *month, int *day, int *hour, int *minute, int *second, int *weekday) {
	struct tm dt = osGetLocalDateTime();
	if(year) *year = dt.tm_year + 1900;
	if(month) *month = dt.tm_mon + 1;
	if(day) *day = dt.tm_mday;
	if(hour) *hour = dt.tm_hour;
	if(minute) *minute = dt.tm_min;
	if(second) *second = dt.tm_sec;
	if(weekday) *weekday = dt.tm_wday;
	return STD_ERR_OK;
}

std_err_t rtc_init() {
	int Status;
	XRtcPsu_Config *Config;

	// Initialize the RTC driver so that it's ready to use
	// Look up the configuration in the config table, then initialize it.
	Config = XRtcPsu_LookupConfig(RTC_DEVICE_ID);
	if (NULL == Config) return STD_ERR_FAIL;

	Status = XRtcPsu_CfgInitialize(&RtcPsu, Config, Config->BaseAddr);
	if (Status != XST_SUCCESS) return STD_ERR_FAIL;

	/* Check hardware build */
	Status = XRtcPsu_SelfTest(&RtcPsu);
	if (Status != XST_SUCCESS) return STD_ERR_FAIL;

	// 设置默认时区为中国大陆
	setenv("TZ", "CST-8", 1);
	tzset();

	logd("Current RTC time: ");
	int year, month, day, hour, minute, second, weekday;
	osGetDateTime(&year, &month, &day, &hour, &minute, &second, &weekday);
	log_printf("%04d-%02d-%02d %02d:%02d:%02d (%d)\r\n", year, month, day, hour, minute, second, weekday);

	return STD_ERR_OK;
}
